package wx

import (
	"context"
	"errors"
	"fmt"
	"log/slog"

	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
	"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
	"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/h5"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
	"github.com/wechatpay-apiv3/wechatpay-go/services/transferbatch"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
)

type WXPayV3 struct {
	MchId            string
	CertKeyPath      string
	CertSerialNumber string
	ApiV3Key         string
	Client           *core.Client
}

func (w *WXPayV3) GetClient() (*core.Client, error) {
	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(w.CertKeyPath)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(w.MchId, w.CertSerialNumber, mchPrivateKey, w.ApiV3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return client, nil
}

func (w *WXPayV3) GetNotifyHandler() (*notify.Handler, error) {
	_, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 获取平台证书访问器
	certVisitor := downloader.MgrInstance().GetCertificateVisitor(w.MchId)
	handler, err := notify.NewRSANotifyHandler(w.ApiV3Key, verifiers.NewSHA256WithRSAVerifier(certVisitor))
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	return handler, nil
}

func (w *WXPayV3) JsPrepay(req jsapi.PrepayRequest) (*jsapi.PrepayWithRequestPaymentResponse, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := jsapi.JsapiApiService{Client: client}
	// 得到prepay_id，以及调起支付所需的参数和签名
	resp, _, err := svc.PrepayWithRequestPayment(ctx, req)
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

func (w *WXPayV3) JsQueryOrderByOutTradeNo(outTradeNo string) (*payments.Transaction, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := jsapi.JsapiApiService{Client: client}
	resp, _, err := svc.QueryOrderByOutTradeNo(ctx,
		jsapi.QueryOrderByOutTradeNoRequest{
			OutTradeNo: core.String(outTradeNo),
			Mchid:      core.String(w.MchId),
		},
	)
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

func (w *WXPayV3) JsCloseOrder(outTradeNo string) error {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return err
	}

	ctx := context.Background()
	svc := jsapi.JsapiApiService{Client: client}
	_, err = svc.CloseOrder(ctx, jsapi.CloseOrderRequest{
		OutTradeNo: core.String(outTradeNo),
		Mchid:      core.String(w.MchId),
	},
	)
	if err != nil {
		slog.Error(err.Error())
		return err
	}

	return nil
}

func (w *WXPayV3) Refund(req refunddomestic.CreateRequest) (*refunddomestic.Refund, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := refunddomestic.RefundsApiService{Client: client}
	resp, _, err := svc.Create(ctx, req)
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

func (w *WXPayV3) RefundQuery(outRefundNo string) (*refunddomestic.Refund, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := refunddomestic.RefundsApiService{Client: client}
	resp, _, err := svc.QueryByOutRefundNo(ctx,
		refunddomestic.QueryByOutRefundNoRequest{
			OutRefundNo: core.String(outRefundNo),
		},
	)
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

// Transfer 批量打款给个人
// 1、打款金额不得低于1元
// 2、当打款金额超过2000元时，必须提供对方的真实姓名
func (w *WXPayV3) Transfer(appId string, outBatchNo string, batchName string, batchRemark string,
	transferDetails []WxTransferDetail) (*transferbatch.InitiateBatchTransferResponse, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := transferbatch.TransferBatchApiService{Client: client}

	var totalAmount int64 = 0
	var transferDetailInputs = make([]transferbatch.TransferDetailInput, 0)
	for _, item := range transferDetails {
		transferDetailInput := transferbatch.TransferDetailInput{
			OutDetailNo:    core.String(item.OutTradeNo),
			TransferAmount: core.Int64(item.Amount),
			TransferRemark: core.String(item.Remark),
			Openid:         core.String(item.OpenId),
		}
		if len(item.UserName) > 0 {
			transferDetailInput.UserName = core.String(item.UserName)
		}
		transferDetailInputs = append(transferDetailInputs, transferDetailInput)
		totalAmount += item.Amount
	}

	resp, _, err := svc.InitiateBatchTransfer(ctx, transferbatch.InitiateBatchTransferRequest{
		Appid:              core.String(appId),
		OutBatchNo:         core.String(outBatchNo),
		BatchName:          core.String(batchName),
		BatchRemark:        core.String(batchRemark),
		TotalAmount:        core.Int64(totalAmount),
		TotalNum:           core.Int64(1),
		TransferDetailList: transferDetailInputs,
		TransferSceneId:    nil,
	})
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

func (w *WXPayV3) TransferQuery(outBatchNo string, page int64, pageSize int64) (*transferbatch.TransferBatchEntity, error) {
	client, err := w.GetClient()
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	ctx := context.Background()
	svc := transferbatch.TransferBatchApiService{Client: client}

	resp, _, err := svc.GetTransferBatchByOutNo(ctx, transferbatch.GetTransferBatchByOutNoRequest{
		OutBatchNo:      core.String(outBatchNo),
		NeedQueryDetail: core.Bool(true),
		Offset:          core.Int64((page - 1) * pageSize),
		Limit:           core.Int64(pageSize),
		DetailStatus:    core.String("ALL"),
	})
	if resp != nil {
		slog.Debug(resp.String())
	}
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	return resp, nil
}

func (w *WXPayV3) TransferQueryAll(outBatchNo string) (*transferbatch.TransferBatchEntity, error) {
	var page int64 = 1
	var pageSize int64 = 100
	resp, err := w.TransferQuery(outBatchNo, page, pageSize)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	for int64(len(resp.TransferDetailList)) < *resp.TransferBatch.TotalNum {
		page++
		resp2, err2 := w.TransferQuery(outBatchNo, page, pageSize)
		if err2 != nil {
			slog.Error(err2.Error())
			return nil, err2
		}
		resp.TransferDetailList = append(resp.TransferDetailList, resp2.TransferDetailList...)
	}

	return resp, nil
}

func (w *WXPayV3) H5Prepay(req h5.PrepayRequest) (*h5.PrepayResponse, error) {
	client, err := w.GetClient()
	if err != nil {
		return nil, err
	}

	svc := h5.H5ApiService{Client: client}
	ctx := context.Background()
	resp, _, err := svc.Prepay(ctx, req)
	fmt.Println(resp)
	if err != nil {
		slog.Error(err.Error())
		return nil, errors.New("调用微信支付失败, err: " + err.Error())
	}

	return resp, nil
}
